#ifndef IP_PACKET_H
#define IP_PACKET_H

#include <stdint.h>
#include <unordered_set>
#include "ip_addr.h"
#include "mac_addr.h"
#include "pkt_meta_def.h"

/*
 * 对IP报文pkt_buf_t内容进行解析处理，将解析结果存到pkt_meta_t 结构中：
 * 1:非TCP/UDP报文直接丢弃
 * 2:对IP分片报文进行重组
 * 3:报文过滤处理
 * 3.1如果报文没有命中资产server，则返回丢弃
 * 3.2命中资产server，单资产没有使能，则返回丢弃
 */

namespace secsmart_tcpip
{
class Packet;
class IPReassembly;

enum ip_pkt_error_code
{
    IP_PKT_OK = 0,
    IP_PKT_ERR,
    IP_PKT_UNSUPPORT, // 不支持的协议报文
    IP_PKT_MALFORMED, // 畸形报文，如报文被截断
    IP_PKT_FRAG_CACHED, // IP分片缓存
    IP_PKT_FRAG_DROP, // 分片报文丢弃
    IP_PKT_FILTER_DROP, // 过滤掉的报文
    IP_PKT_DROP_MAX,
};

typedef struct ip_pkt_stat {
    uint64_t rcv_pkt_num;
    uint64_t rcv_pkt_bytes;
    uint64_t snd_pkt_num;
    uint64_t snd_pkt_bytes;
    uint64_t drop_pkt_num[IP_PKT_DROP_MAX];

    void stat_rcv_pkt(uint32_t pkt_len)
    {
        rcv_pkt_num++;
        rcv_pkt_bytes += pkt_len;
    }

    void stat_snd_pkt(uint32_t pkt_len)
    {
        snd_pkt_num++;
        snd_pkt_bytes += pkt_len;
    }

    void stat_drop_pkt(uint32_t type)
    {
        if (type >= IP_PKT_DROP_MAX) {
            return;
        }
        drop_pkt_num[type]++;
    }

    void stat_clear()
    {
        rcv_pkt_num = 0;
        rcv_pkt_bytes = 0;
        snd_pkt_num = 0;
        snd_pkt_bytes = 0;
        for (int i = 0; i < IP_PKT_DROP_MAX; i++) {
            drop_pkt_num[i] = 0;
        }
    }
}ip_pkt_stat_t;

class IpPacketHandler
{
public:
    IpPacketHandler();
    ~IpPacketHandler();
    void init();

    /*
     * 报文处理接口。
     * 1、解析输入的报文，将解析结果存放于meta中。
     * 2、ip分片报文进行重组，重组成功时，输出重组后的报文
     * @param[in] in_pkt:报文内容起始地址
     * @param[in/out] meta:报文基本信息，作为输入参数时，需要携带报文长度、报文链路类型、报文的时间戳
     * @param[out] out_pkt:处理后的报文内容地址，当返回值不为0时，该值没有意义。 
     *                     当返回值为0时，对于非分片报文，out_pkt和in_pkt相等。
     *                     分片报文out_pkt为重组后的报文，对应的报文内存不需要外部释放。
     * @return：0:成功，报文需要进行后续处理。非零，报文不需要在进行后续处理。
     */
    uint32_t process_packet(uint8_t *in_pkt, uint8_t **out_pkt, packet_meta_t *meta);

    /*
     * 设置分片重组使能开关
     */
    void set_frag_reassembly_enable(bool on_off);

    bool get_frag_reassembly_enable() { return m_enable_frag_reassembly_; }

    /*
     * 设置分片重组的配置参数
     */
    void set_frag_reassembly_cfg(uint32_t ip_stream_size, uint32_t frag_cache_size, uint32_t timeout, bool detect_ooo);

    /*
     * 设置分片重组的配置参数
     */
    void get_frag_reassembly_cfg(uint32_t &ip_stream_size, uint32_t &frag_cache_size, uint32_t &timeout, bool &detect_ooo);

    IPReassembly *get_frag_handler() { return m_frag_handler_;}

    ip_pkt_stat_t get_pkt_stat() { return m_stat; }
    
private:
    bool m_enable_frag_reassembly_;
    bool m_enable_filter_packet;
    IPReassembly *m_frag_handler_; // IP分片重组处理器
    ip_pkt_stat_t m_stat;
    Packet *m_re_pkt_; // 记录重组后的报文 

    bool filter_packet(packet_meta_t *meta);
    uint32_t process_frag_packet(Packet *pkt);
    bool fast_parse_packet(uint8_t *in_pkt, packet_meta_t *meta);
};

}
#endif // IP_PACKET_H

